<?PHP

class sdl
{
    private $screen = null;
    private $w;
    private $h;
    private $bcolor = 0x0;
    private $kb = array();
    private $quit = TRUE;
    const SDL_EXIT = 'sdl_exit';

    static public function error($str = "")
    {
        echo $str, PHP_EOL;
        return FALSE;
    }

    public function __construct($w = 640, $h = 480, $bcolor = 0x0, $flag = SDL_SWSURFACE)
    {
        if (!extension_loaded("sdl")) {
           if (!dl("sdl.so"))
               return self::error("sdl扩展加载失败!");
        }

        if (SDL_Init(SDL_INIT_VIDEO))
            return self::error("sdl 初始化失败!");

        $this->w = $w;
        $this->h = $h;
        $this->bcolor = $bcolor;

        $this->screen = SDL_SetVideoMode($this->w, $this->h, 32, intval($flag));

        if ($this->screen == null)
            return self::error("窗口创建失败!");

        $this->keys(SDLK_ESCAPE, array('sdl', 'quit'));

        $this->rect(0, 0, $this->w, $this->h, $this->bcolor);
        $this->flip();

        return TRUE;
    }

    public function setBackColor($color)
    {
        $this->bcolor = $color;
    }

    public function __get($name)
    {
        return $this->$name;
    }

    public function bg($color = null)
    {
        if ($color === null)
            $color = $this->bcolor;
        $this->rect(0, 0, $this->w, $this->h, $color);
    }

    public function keys($key, $call, & $data = null, $status = SDL_KEYDOWN)
    {
        if ($key < SDLK_FIRST || $key > SDLK_LAST)
            return self::error("key参数传递有误!");

        if (!($status == SDL_KEYDOWN
                || $status == SDL_KEYUP))
            return self::error("status参数值必须为SDL_KEYDOWN或SDL_KEYUP!");

        $this->kb[$status][$key]['call'] = $call;
        $this->kb[$status][$key]['data'] = & $data;

        return TRUE;
    }

    public function quit()
    {
        $this->quit = FALSE;
    }

    private function keyhandle($index, & $event, & $e)
    {
        $key = $event['key']['keysym']['sym'];
        $type = $event['type'];

        if ($index != null && !empty($e[$index]))
            $e[$index]($key , $e['data']);
        else {
            $call = $this->kb[$type][$key]['call'];
            if ($call != null) {
                if ($call == SDL_EXIT) {
                    $this->quit();
                } else {
                    // call_user_func($call, $key, $type, $this->kb[$type][$key]['data']); //无法引用传递参数
                    call_user_func_array($call, array($key, $type, & $this->kb[$type][$key]['data']));
                }
            }

        }
    }

    public function run($e = array())
    {
        $eventType = [
            SDL_KEYDOWN => 'keydown',
            SDL_KEYUP => 'keyup'
        ];

        while ($this->quit)
        {
            if (SDL_WaitEvent($event))
            {
                $index = $eventType[$event['type']];

                if ($event['type'] == SDL_QUIT)
                    $this->quit();
                else if ($event['type'] == SDL_KEYDOWN
                        || $event['type'] == SDL_KEYUP) {
                    $this->keyhandle($index, $event, $e);
                }
            }
        }
    }

    static public function randColor()
    {
        $color[0] = mt_rand(0, 0xFF);
        $color[1] = mt_rand(0, 0xFF);
        $color[2] = mt_rand(0, 0xFF);

        return $color;
    }

    private function getColor($color = null, $dst = null)
    {
        if ($dst === null)
            $dst = $this->screen;

        if ($color === null) {
            $color = self::randColor();
        }
        if (!is_array($color)) {
            $c = $color;
            $color = [
                ($c >> 16) & 0xFF,
                ($c >> 8) & 0xFF,
                $c & 0xFF
            ];
        }

        return SDL_MapRGB($dst['format'], $color[0], $color[1], $color[2]);
    }

    public function rect($x, $y, $w, $h, $color = null, $dst = null)
    {
        if ($dst === null)
            $dst = $this->screen;

        $rect = [
            'x' => $x,
            'y' => $y,
            'w' => $w,
            'h' => $h
        ];

        SDL_FillRect($dst, $rect, $this->getColor($color, $dst));
    }

    public function px($x, $y, $color = null, $dst = null)
    {
        $this->rect($x, $y, 1, 1, $color, $dst);
    }

    public function vline($x, $y, $len, $color = null, $dst = null)
    {
        $this->rect($x, $y, 1, $len, $color, $dst);
    }

    public function hline($x, $y, $len, $color = null, $dst = null)
    {
        $this->rect($x, $y, $len, 1, $color, $dst);
    }

    public function box($x, $y, $w, $h, $border = 1, $border_color = null, $bcolor = null, $dst = null)
    {
        if ($dst === null)
            $dst = $this->screen;

        $this->rect($x, $y, $w, $h, $border_color, $dst);

        if ($bcolor === null)
            $bcolor = $this->bcolor;

        $space = $border * 2;
        $this->rect($x + $border, $y + $border, $w - $space, $h - $space, $bcolor);
    }

    public function circle($x, $y, $r, $color = null, $dst = null)
    {
        if ($dst === null)
            $dst = $this->screen;

        $sx = $x - $r;
        $sy = $y - $r;
        $ex = $x + $r;
        $ey = $y + $r;

        if ($color === null)
            $color = self::randColor();

        for ($i = $sy; $i < $ey; ++$i) {
            for ($j = $sx; $j < $ex; ++$j) {
                if (($i - $y) * ($i - $y) + ($j - $x) * ($j - $x) <= $r * $r)
                   $this->px($j, $i, $color, $dst);
            }
        }
    }

    public function flip($dst = null)
    {
        if ($dst === null)
            $dst = $this->screen;

        SDL_Flip($dst);
    }

    public function __destruct()
    {
        SDL_FreeSurface($this->screen);
        SDL_Quit();
    }
}

////////////////////////////////////////////////////////
// $sdl = new sdl();

// echo $sdl->w, PHP_EOL;

/*
 * function mykey($key, $status, & $data)
 * {
 *     $data['sdl']->bg();
 *     $data['sdl']->circle($data['x'], $data['y'], $data['r']);
 *     $data['sdl']->flip();
 *     $data['x'] += 10;
 *     $data['y'] += 10;
 * }
 *
 * $sdl = new sdl();
 *
 * $x = 100;
 * $y = 100;
 * $r = 50;
 *
 * $sdl->circle($x, $y, $r);
 * $sdl->flip();
 *
 * $data = array(
 *     'sdl' => $sdl,
 *     'x' => $x,
 *     'y' => $y,
 *     'r' => $r
 * );
 *
 * $sdl->keys(SDLK_q, "mykey", $data);
 * $sdl->keys(SDLK_w, "mykey", $data);
 *
 * $sdl->run();
 */

